<!--
 * @Description: 
 * @Author: huxianc
 * @Date: 2020-11-16 10:04:31
 * @LastEditors: huxianc
 * @LastEditTime: 2020-11-18 10:53:19
-->
<template>
  <div id="app-1">
    <div id="container"></div>
    <div id="preview"></div>
    <div
      class="context-menu"
      :style="{
        left: `${contextMenuStyle.left}px`,
        top: `${contextMenuStyle.top}px`
      }"
    >
      <div
        v-for="menuItem in contextMenuList"
        :key="menuItem.label"
        class="menu-item"
        @click="handleClickContentMenu(menuItem.clickCode)"
      >
        {{ menuItem.label }}
      </div>
    </div>
  </div>
</template>

<script>
import Konva from "konva";
import { DEFAULT_COLORS } from "../utils/canvas/const_colors"; // 颜色
import { stageWH, stageConfig } from "../utils/canvas/stage_config"; // stage的配置参数
import { locateIconConfig } from "../utils/canvas/locateIcon_config";
import { tooltipConfig } from "../utils/canvas/tooltip_config";
import { bubbleConfig } from "../utils/canvas/bubble_config";
import { personConfig, movePersonConfig } from "../utils/canvas/person_config";
import { isInScreen, loadImages } from "../utils/canvas"; // canvas页面工具函数

const MIN_SCALE = 0.32;
const MAX_SCALE = 1;
export default {
  name: "dash",
  components: {},
  data() {
    return {
      websock: null,
      loading: {
        socket: null,
        image: null
      }, // element-ui 指令方式 loading  https://element.eleme.cn/2.13/#/zh-CN/component/loading
      loadingStatus: false, // 所有图片和shape是否加载完毕
      sceneCode: "scene-0", // 场景编号
      imageSource: null, // 所有图片资源
      stage: null,
      layer: null,
      personList: [],
      personIndex: 0,
      previewStage: null, // 预览舞台
      previewLayer: null, // 预览图层
      scale: stageConfig.scale, // 页面缩放
      imgX: 0, // 偏移量 [stageWH.width*scale - window.innerWidth,0]
      imgY: 0, // [stageWH.height*scale - window.innerHeight,0]
      draggable: false, // stage是否可以拖拽移动
      pos: null,
      posTmp: null, // 临时存放位置
      allTweens: {
        personTween: null, // 人物移动
        stageTween: null, // 舞台移动
        lineTween: null // 线移动
      },
      lineNode: null, // 保存人物移动下面那条线的节点
      linePos: [], // 存放line位置的数组 line根据这个绘制直线
      // 右键菜单列表
      contextMenuList: [
        {
          label: "实时监控",
          clickCode: "spyOn"
        },
        {
          label: "查看回放",
          clickCode: "seeBack"
        }
      ],
      // 右键菜单的位置
      contextMenuStyle: {
        left: -99999,
        top: -99999
      },
      // ! 目前这个变量无用
      isDrawFinished: false // 画布及页面元素是否绘制完毕
    };
  },
  created() {
    // 获取场景编号
    this.sceneCode = this.$route.query.sceneCode || "scene-0";
  },
  async mounted() {
    this.loading.socket = this.initLoading("建立websocket连接中...");
    // Todo 建立websocket 从路由获取场景编号 并通过websocket发送场景编号
    await this.initWebsocket();

    this.loading.socket.close();

    // todo 遍历后端的数据，将图片全部找出来放到一个数组中，后端应该返回的是url地址
    // const imageSource = {
    //   bg: "bg.jpg",
    //   "bg-1": "bg-1.jpg"
    // };
    const url = "http://127.0.0.1:5500/imgs/";
    // const url =
    //   "https://uploadbeta.com/api/pictures/random/?key=BingEverydayWallpaperPicture";
    const imageSource = {
      bg: url + "bg.jpg",
      "bg-1": url + "bg-1.jpg"
    };

    this.loading.image = this.initLoading("加载图片中...");

    await loadImages(imageSource, this.startInit);

    console.log(this.$route);
  },
  methods: {
    // websocket 相关
    initWebsocket(url = "49.234.18.41:8866") {
      this.websock = new WebSocket(`ws://${url}`);
      this.websock.onmessage = this.websocketonmessage;
      this.websock.onopen = this.websocketonopen;
      // this.websock.onerror = this.websocketonerror;
      this.websock.onclose = this.websocketclose;
    },
    websocketonopen() {
      //连接建立之后执行send方法发送数据
      const actions = { sceneCode: this.sceneCode };
      this.websocketsend(JSON.stringify(actions));
    },
    websocketonerror() {
      //连接建立失败重连
      this.initWebSocket();
    },
    websocketonmessage(e) {
      console.log(e);

      // * 这里的实现写在一个下面的定时器内
      // todo 后端给我返得数据 应该就是新增了xxx，少了xxx，移动了xxx 我想象中就是一个对象
      // ? {add:[],del:[],move:[]}
      // ? add 的话，循环将数据push进personList并调用初始化函数加入layer

      // ? del的话，循环在personList找到并删除，还要在layer中销毁

      // ? move 直接调personMove方法
    },
    websocketsend(data) {
      //数据发送
      this.websock.send(data);
    },
    websocketclose(e) {
      //关闭
      console.log("断开连接", e);
    },
    initLoading(loadingText) {
      const loading = this.$loading({
        lock: true,
        text: loadingText,
        spinner: "el-icon-loading",
        background: "rgba(0, 0, 0, 0.7)"
      });
      return loading;
    },
    /**
     * @description: 整个页面等所有图片加载完后开始绘制
     * @param {object} source 所有图片加载完成后返回的图片资源对象
     * @return {void} undefined
     */

    async startInit(source) {
      this.imageSource = source || null;
      this.loading.image.close();
      this.initStage();
      this.initLayer();

      const src =
        this.sceneCode === "scene-0"
          ? source.bg.currentSrc
          : source["bg-1"].currentSrc;
      await this.initBgImage(src);
      this.initLoacateIcon({ x: 200, y: 300 });
      this.initTooltip({ x: 300, y: 100 });
      this.initBubble({ x: 160, y: 160 });

      for (let i = 0; i < 50; i++) {
        this.personIndex++;
        this.personList.push({
          x: Math.random() * stageWH.width,
          y: Math.random() * stageWH.height,
          name: "person-" + this.personIndex,
          text: this.personIndex
        });
      }
      this.personList.forEach(item => {
        this.initPerson(item);
      });

      // 模拟数据
      setInterval(() => {
        if (this.personList.length > 70) return;
        this.personList.forEach(item => {
          const randomNum = Math.random();
          if (randomNum < 0.05) {
            // 新增
            this.personIndex++;
            const newItem = Object.assign({}, item, {
              x: Math.random() * stageWH.width,
              y: Math.random() * stageWH.height,
              name: "person-" + this.personIndex,
              text: this.personIndex
            });
            this.personAdd(newItem);
          } else if (randomNum < 0.1) {
            // 删除
            const delIndex = Math.floor(Math.random() * this.personIndex + 1);
            this.personDel("person-" + delIndex);
          } else if (randomNum < 0.9) {
            // do nothing
          } else {
            // 移动
            item.needUpdate = true;
            item.nextPos = {
              x: Math.random() * stageWH.width,
              y: Math.random() * stageWH.height
            };
            const name = this.stage.findOne("." + item.name);
            name && this.personMove(name, item.nextPos);
          }
        });
      }, 1000);
    },
    initStage() {
      this.imgX = (-stageWH.width * this.scale) / 2 + window.innerWidth / 2;
      this.imgY = (-stageWH.height * this.scale) / 2 + window.innerHeight / 2;
      this.stage = new Konva.Stage({
        x: this.imgX,
        y: this.imgY,
        container: "container",
        // draggable: true
        scaleX: this.scale,
        scaleY: this.scale,
        ...stageWH
      });

      this.stage.on("contextmenu", e => {
        e.evt.preventDefault();
        // this.contextMenuStyle.left = e.evt.clientX + 4;
        // this.contextMenuStyle.top = e.evt.clientY + 4;
      });

      this.canvasEventsInit();
    },
    initLayer() {
      this.layer = new Konva.Layer();
      this.stage.add(this.layer);
    },
    /**
     * @description: 初始化预览舞台，气死，这个要放在最后  我找了半天
     * @param {*}
     * @return {void} undefined
     */
    initPreview() {
      const previewScale = 0.1;
      this.previewStage = new Konva.Stage({
        container: "preview",
        width: 600,
        height: 300,
        // scaleX: 1 / 4,
        // scaleY: 1 / 4
        scaleX: previewScale,
        scaleY: previewScale,
        opacity: 0.5
      });

      this.previewLayer = this.layer.clone({ listening: false });

      // let draggable = false;
      // this.previewLayer = this.layer.clone();
      // this.previewLayer.on("mousedown", e => {
      //   console.log(e);
      //   draggable = true;
      // });
      // this.previewLayer.on("mouseup", e => {
      //   console.log(e);
      //   draggable = false;
      // });
      // this.previewLayer.on("mousemove", e => {
      //   if (!draggable) return;
      //   console.log(e);
      //   this.previewStage.to({
      //     x: e.evt.layerX / previewScale,
      //     y: e.evt.layerY / previewScale
      //   });
      //   this.previewLayer.batchDraw();
      // });
      // console.log(this.previewLayer);

      this.previewStage.add(this.previewLayer);
    },
    /**
     * @description: 更新预览视图，通过将layer中的循环，然后在previewLayer中找到，进行位置更新
     * @param {*}
     * @return {void}
     */

    updatePreview() {
      if (!this.previewLayer) return;
      this.layer.children.forEach(shape => {
        // console.log(shape);
        // console.log(shape.name());
        // console.log(this.previewLayer);
        const clone = this.previewLayer.findOne("." + shape.name());
        clone && clone.position(shape.position());
      });
      this.previewLayer.batchDraw();
    },
    /**
     * @description: 当有shape增加或者减少后调用，会销毁之前的previewLayer并重新克隆一份
     * @param {*}
     * @return {void}
     */

    updatePreviewByNumChange() {
      if (!this.previewStage) return;
      this.previewLayer && this.previewLayer.destroy();
      this.previewLayer = this.layer.clone({
        listening: false
      });
      this.previewStage.add(this.previewLayer);
    },
    /**
     * @description: 绘制背景图，目前发现所有的shape必须在背景图绘制完成后再绘制才会在stage中显示，不然会被背景图片挡住 -> 发现了moveToBottom可以将图片放到最底层
     * @param {*}
     * @return {void} undefined
     */

    async initBgImage(src) {
      const bg = new Image();
      bg.onload = async () => {
        const image = new Konva.Image({
          image: bg,
          ...stageWH,
          name: "bg-img"
        });

        await this.layer.add(image);
        image.moveToBottom(); // 通过这个可以将图层移动最底层
        await this.layer.batchDraw();

        // ? 这个好像必须放在这，提出去会报错
        // await this.initPreview();

        this.isDrawFinished = true;

        // 移动中的人物监听
        const needRunLinePoints = JSON.parse(
          localStorage.getItem("needRunLinePoints")
        );
        if (needRunLinePoints && needRunLinePoints.length > 0) {
          this.$message.info("检测到有查看回放的人物，正在恢复中...");
          setTimeout(() => {
            this.initMovePerson(needRunLinePoints);
          }, 2000);
        }
      };
      // http 地址需要 currentSrc
      bg.src = src;
    },

    /**
     * @description: 当前页面的stage位置修正
     * @return {void} undefined
     */

    correctStage() {
      const position = isInScreen(this.imgX, this.imgY, this.scale);
      this.imgX = position.x;
      this.imgY = position.y;
    },

    /**
     * @description: 绘制类似于定位的气泡，并加入到layer中
     * @param {object} attrs {x,y}
     * @param {string} fillColor 填充颜色
     * @param {string} text 显示文本
     * @return {void} undefined
     */

    initLoacateIcon(
      attrs = {},
      fillColor = DEFAULT_COLORS.GREEN,
      text = "测试"
    ) {
      const { x, y } = attrs;
      const { r, distance } = locateIconConfig;
      const h = r + distance;

      const locateBubbleGroup = new Konva.Group({
        name: "locateBubbleGroup"
      });
      const locateBubbleShape = new Konva.Shape({
        sceneFunc: function(ctx, shape) {
          ctx.beginPath();
          ctx.moveTo(x, y);
          ctx.arc(x, y, r, Math.PI / 4, (Math.PI / 4) * 3, true);

          const R = (Math.SQRT2 * r) / 2; // 算出来的偏移量

          ctx.lineTo(x, y + h);
          ctx.lineTo(x + R, y + R);

          ctx.closePath();

          // (!) Konva specific method, it is very important
          ctx.fillStrokeShape(shape);
        },
        fill: fillColor,
        shadowColor: "rgba(0, 0, 0, 0.2)",
        shadowBlur: 10
      });
      const locateBubbleText = new Konva.Text({
        x: x,
        y: y,
        text: text,
        fontSize: 14,
        fill: "#fff"
      });
      locateBubbleGroup.add(locateBubbleShape);
      locateBubbleText.offsetX(locateBubbleText.width() / 2);
      locateBubbleGroup.add(locateBubbleText);
      this.layer.add(locateBubbleGroup);
    },

    /**
     * @description: tooltip 他的宽高是由内容+padding撑开的 x y 是中间那个小角的坐标， 绘制tooltip并加入到layer中
     * @param {object} attrs 坐标 {x,y}
     * @param {string} fillColor 填充颜色
     * @param {string} text tooltip文字
     * @return {void} undefined
     */

    initTooltip(
      attrs = {},
      fillColor = DEFAULT_COLORS.GREEN,
      text = "测试12321312adfdasfas"
    ) {
      const { x, y } = attrs;
      const group = new Konva.Group({
        name: "tooltip"
      });
      const tooltip = new Konva.Label({
        x,
        y
      });

      const tag = new Konva.Tag({
        fill: fillColor,
        ...tooltipConfig
      });

      tooltip.add(tag);
      tooltip.add(
        new Konva.Text({
          text: text,
          fontSize: 14,
          padding: 10,
          fill: "#fff"
        })
      );

      group.add(tooltip);
      this.layer.add(group);
    },
    /**
     * @description: 绘制上有文字 下有数字的气泡 并加入到layer中
     * @param {object} attrs {x,y}
     * @param {string} fillColor 填充颜色
     * @param {string} topText 上部显示的文字
     * @param {string | number} bottomText 下部显示的文字
     * @return {void} undefined
     */

    initBubble(
      attrs = {},
      fillColor = DEFAULT_COLORS.GREEN,
      topText = "test",
      bottomText = "12"
    ) {
      const { x, y } = attrs;
      const { w, h, R } = bubbleConfig;
      const group = new Konva.Group({
        name: "bubble"
      }); // 最外层的组
      const box = new Konva.Shape({
        sceneFunc: function(ctx, shape) {
          ctx.beginPath();
          ctx.moveTo(x + R, y);
          ctx.arcTo(x + w, y, x + w, y + h, R);
          ctx.arcTo(x + w, y + h, x, y + h, R);
          ctx.lineTo(x + w, y + h);
          ctx.lineTo(x + w / 2 + 2 * R, y + h);
          ctx.lineTo(x + w / 2, y + h + R);
          ctx.lineTo(x + w / 2 - 2 * R, y + h);
          ctx.arcTo(x, y + h, x, y, R);
          ctx.arcTo(x, y, x + w, y, R);
          ctx.closePath();
          ctx.fillStrokeShape(shape);
        },
        fill: fillColor,
        shadowColor: "rgba(0,0,0,0.2)",
        shadowBlur: 10
      });

      // 上部显示的文字
      const label = new Konva.Text({
        x: x + w / 2,
        y: y + h / 8,
        text: topText,
        fontSize: 14,
        fill: "#fff"
      });
      label.offsetX(label.width() / 2);

      const line = new Konva.Line({
        points: [x, y + h / 2, x + w, y + h / 2],
        stroke: "#fff",
        strokeWidth: 0.5
      });
      group
        .add(box)
        .add(label)
        .add(line);

      // 下部圆角矩形的组
      const cornerGroup = new Konva.Group();
      const cornerRect = new Konva.Rect({
        x: x + w / 2,
        y: y + h * 0.6,
        width: w / 3,
        height: (h / 2) * 0.7,
        fill: "rgba(255,255,255,.6)",
        cornerRadius: (h / 2) * 0.7
      });
      cornerRect.offsetX(cornerRect.width() / 2);
      cornerGroup.add(cornerRect);

      const cornerText = new Konva.Text({
        x: x + w / 2,
        y: y + h * 0.8,
        fill: DEFAULT_COLORS.GREEN,
        text: bottomText
      });
      cornerText.offsetX(cornerText.width() / 2);
      cornerText.offsetY(cornerText.height() / 2);
      cornerGroup.add(cornerText);

      group.add(cornerGroup);

      // 事件注册
      let timer = null;

      const clickEvent = e => {
        clearTimeout(timer);
        timer = setTimeout(() => {
          console.log(e);
          console.log("clicked");
          e.target.setAttrs({
            shadowColor: "red",
            shadowOpacity: 0.8
          });
          this.layer.batchDraw();
        }, 300);
      };

      const dblclickEvent = e => {
        clearTimeout(timer);
        console.log(e);
        console.log("dblclicked");
      };

      const contextmenukEvent = e => {
        clearTimeout(timer);
        console.log(e);
        console.log("contextmenu");
      };

      group.on("click", e => {
        clickEvent(e);
      });

      group.on("dblclick", e => {
        dblclickEvent(e);
      });

      group.on("contextmenu", e => {
        contextmenukEvent(e);
      });

      group.on("mouseover", e => {
        console.log(e);
        console.log("mouseover");
      });

      group.on("mouseout", e => {
        console.log(e);
        console.log("mouseout");
      });

      this.layer.add(group);
    },

    /**
     * @description: 创建普通移动的人物
     * @param {object} attrs {x,y,name}
     * @param {string} fillColor 填充颜色
     * @return {void} undefined
     */

    initPerson(attrs = {}, fillColor = DEFAULT_COLORS.GREEN_ALPHA_5) {
      const { x, y, name, text } = attrs;
      const { r, distance } = personConfig;
      const h = r + distance;

      const personGroup = new Konva.Group({
        x,
        y,
        name
      });
      const person = new Konva.Shape({
        sceneFunc: function(ctx, shape) {
          ctx.beginPath();
          ctx.moveTo(0, 0);
          ctx.arc(0, 0, r, Math.PI / 4, (Math.PI / 4) * 3, true);

          const R = (Math.SQRT2 * r) / 2; // 算出来的偏移量

          ctx.lineTo(0, 0 + h);
          ctx.lineTo(0 + R, 0 + R);

          ctx.closePath();

          ctx.fillStrokeShape(shape);
        },
        fill: fillColor,
        shadowColor: "rgba(0, 0, 0, 0.2)",
        shadowBlur: 10
      });

      const personText = new Konva.Text({
        text,
        fill: "#fff",
        fontSize: 14
      });
      personText.offset({
        x: personText.width() / 2,
        y: personText.height() / 2
      });

      personGroup.add(person).add(personText);

      personGroup.on("contextmenu", e => {
        e.evt.preventDefault();
        this.contextMenuStyle.left = e.evt.clientX + 4;
        this.contextMenuStyle.top = e.evt.clientY + 4;
      });
      this.layer.add(personGroup);
      // return personGroup;
    },
    personTo() {
      // todo  这里就专门做移动的函数，到时候应该是传入一个对象，然后根据这个对象来做移动
      // ? 下面写了move方法
      // * 这个之前测试的数据调用，现在也用不上了，可以删除
      this.personList.forEach(personAttrs => {
        if (!personAttrs.needUpdate) return;
        const person = this.stage.findOne("." + personAttrs.name);
        person &&
          person.to({
            x: personAttrs.nextPos.x,
            y: personAttrs.nextPos.y,
            onUpdate: () => {
              this.updatePreview();
            },
            duration: 1
          });
      });
    },

    /**
     * @description: 新增人物
     * @param {object} attrs 和初始化人物的一样
     * @return {*}
     */

    personAdd(attrs) {
      this.personList.push(attrs);
      this.initPerson(attrs);

      this.updatePreviewByNumChange();
    },
    /**
     * @description: 删除人物
     * @param {string} name 传入需要删除人物的name
     * @return {*}
     */

    personDel(name) {
      const name1 = this.stage.findOne("." + name);
      if (!name1) return;
      name1.destroy();
      const delIndex = this.personList.findIndex(item => item.name === name);
      this.personList.splice(delIndex, 1);

      this.updatePreviewByNumChange();
    },

    /**
     * @description: 人物位置更新
     * @param {object} needMovePerson 一个shape实例
     * @param {object} attrs {x,y}
     * @param {number} duration 移动时间
     * @return {void}
     */

    personMove(needMovePerson, attrs, duration = 1) {
      const { x, y } = attrs;
      needMovePerson.to({
        x,
        y,
        onUpdate: () => {
          // todo 这样更新感觉性能损耗有点大，在考虑要不要重写一个函数
          this.updatePreview();
        },
        duration
      });
    },
    initMovePerson(posArr = []) {
      // 获取第一个位置
      const { x, y, sceneCode } = posArr[0];
      if (sceneCode !== this.sceneCode) {
        localStorage.setItem("needRunLinePoints", JSON.stringify(posArr));
        this.$message.info("该人物即将跳转到相应场景...");
        this.$router.push({
          path: "/",
          query: {
            sceneCode
          }
        });
        return;
      }
      // console.log("x", x);
      // console.log("imgx", this.imgX);

      this.imgX = -x + window.innerWidth / 2 / this.scale;
      this.imgY = -y + window.innerHeight / 2 / this.scale;
      console.log("x", x);
      console.log("imgx", this.imgX);
      this.correctStage();
      this.stage.to({
        x: this.imgX,
        y: this.imgY,

        duration: 0.3,
        onFinish: () => {
          const { r, distance } = movePersonConfig;
          const h = r + distance;

          const movePersonGroup = new Konva.Group({
            x,
            y,
            name: "movePerson"
          });
          const movePerson = new Konva.Shape({
            sceneFunc: function(ctx, shape) {
              ctx.beginPath();
              ctx.moveTo(0, 0);
              ctx.arc(0, 0, r, Math.PI / 4, (Math.PI / 4) * 3, true);

              const R = (Math.SQRT2 * r) / 2; // 算出来的偏移量

              ctx.lineTo(0, 0 + h);
              ctx.lineTo(0 + R, 0 + R);

              ctx.closePath();

              ctx.fillStrokeShape(shape);
            },
            fill: "rgba(255,0,0,1)",
            shadowColor: "rgba(0, 0, 0, 0.2)",
            shadowBlur: 10
          });

          movePersonGroup.add(movePerson);
          this.layer.add(movePersonGroup);
          // movePersonGroup.moveToTop();

          this.tweenAnimation(movePersonGroup, posArr);
        }
      });
    },
    initMoveLine() {
      this.lineNode && this.lineNode.destroy();
      this.lineNode = new Konva.Line({
        points: this.linePos,
        stroke: "rgba(0,0,0,.3)",
        strokeWidth: 3,
        lineCap: "round",
        lineJoin: "round",
        name: "moveLine"
      });

      this.layer.add(this.lineNode);
    },
    /**
     * @description: 人物移动的动画平移
     * @param {konva返回的node} node 平移的节点
     * @param {array} coordinateArr 平移的坐标点集合 {x,y}
     * @param {number} duration 时间 s
     * @return {*}
     */

    tweenAnimation(node, coordinateArr = [], duration = 1) {
      console.log("coordinateArr", coordinateArr);
      const len = coordinateArr.length;
      if (len === 0) {
        localStorage.removeItem("needRunLinePoints");
        return;
      }

      // 获取第一个位置
      const { sceneCode } = coordinateArr[0];

      if (sceneCode === this.sceneCode) {
        const { x, y } = coordinateArr.shift();
        // 当前人物的位置
        const currentX = node.x();
        const currentY = node.y();

        const distanceX = x - currentX;
        const distanceY = y - currentY;

        this.imgX -= distanceX;
        this.imgY -= distanceY;
        let { personTween, stageTween } = this.allTweens;

        this.correctStage();

        stageTween = new Konva.Tween({
          node: this.stage,
          duration,
          x: this.imgX,
          y: this.imgY,
          onUpdate: () => {
            this.updatePreview();
          },
          onFinish: () => {
            this.stage.batchDraw();
          }
        });

        personTween = new Konva.Tween({
          node,
          duration,
          x: x,
          y: y,
          onUpdate: () => {
            const moveX = node.x();
            const moveY = node.y() + 30;
            this.linePos.push(moveX, moveY);
            // 5 个点产生了204 / 2 个坐标
            this.initMoveLine();
            this.updatePreview();
          },
          onFinish: () => {
            stageTween.pause();

            this.tweenAnimation(node, coordinateArr, duration);
          }
        });
        stageTween.play();
        personTween.play();
      } else {
        console.log("not same scene");
        localStorage.setItem(
          "needRunLinePoints",
          JSON.stringify(coordinateArr)
        );
        this.$router.push({
          path: "/",
          query: {
            sceneCode
          }
        });
      }
    },
    /**
     * @description: 隐藏右键
     * @param {*}
     * @return {*}
     */

    hideContentMenu() {
      this.contextMenuStyle.left = -999999;
      this.contextMenuStyle.top = -999999;
    },
    handleClickContentMenu(code) {
      console.log(code);
      if (code === this.contextMenuList[0].clickCode) {
        // 实时监控
      } else {
        // 查看回放
        // ? mock data
        const len = 10;
        const posArr = [];
        const x = stageWH.width * Math.random();
        const y = stageWH.height * Math.random();
        for (let i = 0; i < len; i++) {
          const flag = Math.random() > 0.5 ? 1 : -1;
          posArr.push({
            x: x + 50 * flag * Math.random() * (i + 1),
            y: y + 70 * flag * Math.random() * (i + 1),
            sceneCode: i > 5 ? "scene-0" : "scene-1"
          });
        }
        this.initMovePerson(posArr);
      }
      this.hideContentMenu();
    },
    /**
     * @description: stage 事件注册
     * @param {*}
     * @return {*}
     */

    canvasEventsInit() {
      this.handleWheel();
      this.handleMouseDown();
      this.handleMouseUp();
      this.handleMouseMove();
    },
    /**
     * @description: 滚轮事件
     * @param {*}
     * @return {*}
     */

    handleWheel() {
      this.stage.on("wheel", e => {
        //
        const wheelDelta = e.evt.wheelDelta
          ? e.evt.wheelDelta
          : e.evt.deltalY * -40;

        // 当前鼠标相对于浏览器窗口的位置
        const pos = {
          x: e.evt.clientX,
          y: e.evt.clientY
        };
        console.log(pos);
        // 根据scale计算出来的新位置
        const newPos = {
          x: ((pos.x - this.imgX) / this.scale).toFixed(2),
          y: ((pos.y - this.imgY) / this.scale).toFixed(2)
        };

        if (wheelDelta > 0) {
          // 放大
          if (this.scale >= MAX_SCALE) {
            this.scale = MIN_SCALE;
            return;
          }
          this.scale += 0.1;
        } else {
          if (this.scale <= MIN_SCALE) {
            this.scale = MIN_SCALE;
            return;
          }
          this.scale -= 0.1;
        }

        this.scale = Number(this.scale.toFixed(2));

        // scale差值 * 新位置 + （老位置 - 新位置）
        this.imgX = (1 - this.scale) * newPos.x + (pos.x - newPos.x);
        this.imgY = (1 - this.scale) * newPos.y + (pos.y - newPos.y);

        this.correctStage();

        this.stage.setAttrs({
          x: this.imgX,
          y: this.imgY,
          scaleX: this.scale,
          scaleY: this.scale
        });
        this.stage.batchDraw();

        console.log(this.stage.scale());
      });
    },
    /**
     * @description: 鼠标按下
     * @param {*}
     * @return {*}
     */

    handleMouseDown() {
      this.stage.on("mousedown", e => {
        this.hideContentMenu();
        if (this.scale === MIN_SCALE) {
          return;
        }

        this.draggable = true;
        this.pos = {
          x: e.evt.clientX,
          y: e.evt.clientY
        };

        this.stage.container().style.cursor = "move";
      });
    },
    /**
     * @description: 鼠标松开
     * @param {*}
     * @return {*}
     */

    handleMouseUp() {
      this.stage.on("mouseup", () => {
        this.draggable = false;
        this.stage.container().style.cursor = "default";
      });
    },
    handleMouseMove() {
      this.stage.on("mousemove", e => {
        if (this.draggable) {
          // console.log("e", e);
          // 临时位置存放鼠标位置
          this.posTmp = {
            x: e.evt.clientX,
            y: e.evt.clientY
          };

          // 计算鼠标位置与上次位置的差值
          let x = this.posTmp.x - this.pos.x;
          let y = this.posTmp.y - this.pos.y;

          // 偏差都为0 则没移动
          if (x === 0 && y === 0) return;

          // 偏移量 += 差值
          this.imgX += x;
          this.imgY += y;

          // console.log("imgx", this.imgX);
          // console.log("imgy", this.imgY);

          // 鼠标位置存放为上次位置
          this.pos = JSON.parse(JSON.stringify(this.posTmp));

          this.correctStage();

          // 舞台位置更新
          this.stage.setAttrs({
            x: this.imgX,
            y: this.imgY
          });
          this.stage.batchDraw();
          // console.log("stagex", this.stage.x());
          // console.log("stageY", this.stage.y());

          this.updatePreview();
        }
      });
    }
  },
  beforeDestroy() {
    this.layer.destroy();
    this.stage.destroy();
    this.websock.close(); //离开路由之后断开websocket连接
  }
};
</script>

<style lang="scss">
#preview {
  position: absolute;
  bottom: 2px;
  left: 2px;
  border: 1px solid grey;
  background-color: lightgrey;
}
.context-menu {
  padding: 10px 0;
  position: absolute;
  z-index: 999999;
  background: #fff;
  border-radius: 4px;
  border: 1px solid #ebeef5;
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
  left: 0px;
  top: 30px;
  .menu-item {
    padding: 0 20px;
    font-size: 14px;
    color: #606266;
    line-height: 36px;
    outline: none;
    cursor: pointer;
    /* & + .menu-item {
      border-top: 1px solid #ccc;
    } */
    &:hover {
      background: #ecf5ff;
      color: #66b1ff;
    }
  }
}
</style>
